package com.cch.utils;


import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class HttpClientUtils {

    protected static Logger logger = (Logger.getLogger(HttpClientUtils.class));

    private static PoolingClientConnectionManager clientConnectionManager;
    /**
     * OK: Success!
     */
    public static final int OK = 200;
    /**
     * Not Modified: There was no new data to return.
     */
    public static final int NOT_MODIFIED = 304;
    /**
     * Bad Request: The request was invalid. An accompanying error message will
     * explain why. This is the status code will be returned during rate
     * limiting.
     */
    public static final int BAD_REQUEST = 400;
    /**
     * Not Authorized: Authentication credentials were missing or incorrect.
     */
    public static final int NOT_AUTHORIZED = 401;
    /**
     * Forbidden: The request is understood, but it has been refused. An
     * accompanying error message will explain why.
     */
    public static final int FORBIDDEN = 403;
    /**
     * Not Found: The URI requested is invalid or the resource requested, such
     * as a user, does not exists.
     */
    public static final int NOT_FOUND = 404;
    /**
     * Not Acceptable: Returned by the Search API when an invalid format is
     * specified in the request.
     */
    public static final int NOT_ACCEPTABLE = 406;
    /**
     * Internal Server Error: Something is broken. Please post to the group so
     * the Weibo team can investigate.
     */
    public static final int INTERNAL_SERVER_ERROR = 500;
    /**
     * Bad Gateway: Weibo is down or being upgraded.
     */
    public static final int BAD_GATEWAY = 502;
    /**
     * Service Unavailable: The Weibo servers are up, but overloaded with
     * requests. Try again later. The search and trend methods use this to
     * indicate when you are being rate limited.
     */
    public static final int SERVICE_UNAVAILABLE = 503;

    public static final int TIME_OUT = 300000;

    public static final int OTHER_IO_EXCEPTION = 498;

    public static final int READ_TIMEOUT = 499;

    public static final int WARN_USE_TIME = 100;

    public static final String SOCKET_TIMEOUT = "http.socket.timeout";
    public static final String COLLECTION_TIMEOUT = "http.connection.timeout";
    public static final String COLLECTION_MANAGER_TIMEOUT = "http.connection-manager.timeout";


    static {
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
        schemeRegistry.register(new Scheme("https", 443, createSSLSocketFactory()));

        clientConnectionManager = new PoolingClientConnectionManager(schemeRegistry);
        clientConnectionManager.setMaxTotal(200);
        clientConnectionManager.setDefaultMaxPerRoute(200);

    }


    @SuppressWarnings("unused")
    private static String getCause(int statusCode) {
        String cause = null;
        switch (statusCode) {
            case NOT_MODIFIED:
                break;
            case BAD_REQUEST:
                cause = "The request was invalid.  An accompanying error message will explain why. This is the status code will be returned during rate limiting.";
                break;
            case NOT_AUTHORIZED:
                cause = "Authentication credentials were missing or incorrect.";
                break;
            case FORBIDDEN:
                cause = "The request is understood, but it has been refused.  An accompanying error message will explain why.";
                break;
            case NOT_FOUND:
                cause = "The URI requested is invalid or the resource requested, such as a user, does not exists.";
                break;
            case NOT_ACCEPTABLE:
                cause = "Returned by the Search API when an invalid format is specified in the request.";
                break;
            case INTERNAL_SERVER_ERROR:
                cause = "Something is broken.  Please post to the group so the liushijie can investigate.";
                break;
            case BAD_GATEWAY:
                cause = "image server is down or being upgraded.";
                break;
            case SERVICE_UNAVAILABLE:
                cause = "Service Unavailable: img servers are up, but overloaded with requests. Try again later. The search and trend methods use this to indicate when you are being rate limited.";
                break;
            default:
                cause = "";
        }

        return statusCode + ":" + cause;
    }


    private static void MonitorTime(String url, long startTime) {
        long useTime = System.currentTimeMillis() - startTime;
        if (useTime > WARN_USE_TIME) {
            logger.warn("url:[" + url + "] http use time:[" + useTime + "] ms,http connection too long");
        } else {
            logger.info("url:[" + url + "] http use time:[" + useTime + "] ms");
        }
    }

    /**
     * 根据URL发送get请求获取数据
     *
     * @param url
     * @return
     */
    public static HttpResult doGet(String url) {
        long currentTime = System.currentTimeMillis();
        HttpResult httpResult = new HttpResult();
        HttpGet get = new HttpGet(url);
        try {
            HttpResponse response = getHttpClient().execute(get);
            HttpEntity resEntity = response.getEntity();
            int statusCode = response.getStatusLine().getStatusCode();
            httpResult.setCode(statusCode);
            httpResult.setBody(getString(resEntity));
            if (statusCode != OK) {
                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
                get.abort();
                return httpResult;
            }
        } catch (SocketTimeoutException e) {
            httpResult.setCode(READ_TIMEOUT);
            return httpResult;
        } catch (IOException e) {
            logger.error("++++ doGet:" + url + " ++++++", e);
            httpResult.setCode(OTHER_IO_EXCEPTION);
            return httpResult;
        } catch (Exception e) {
            logger.error("++++ doPost:" + url + " ++++++", e);
            httpResult.setCode(INTERNAL_SERVER_ERROR);
            return httpResult;
        } finally {
            get.releaseConnection();
            MonitorTime(url, currentTime);
        }
        return httpResult;
    }

    public static HttpResult doGet(String url, Map<String, String> paramsMap) {
        if (null!=paramsMap&&!paramsMap.isEmpty()) {
            StringBuffer param = new StringBuffer();
            int i = 0;
            for (String key : paramsMap.keySet()) {
                if (i == 0)
                    param.append("?");
                else
                    param.append("&");
                param.append(key).append("=").append(SysUtil.truncValue(paramsMap.get(key)));
                i++;
            }
            url += param;
        }
        return doGet(url);
    }

    /**
     * 根据URL发送post请求获取数据
     *
     * @param url
     * @param paramsMap
     * @return
     */
    public static HttpResult doPost(String url, Map<String, String> paramsMap) {
        long currentTime = System.currentTimeMillis();
        HttpResult httpResult = new HttpResult();
        HttpPost post = new HttpPost(url);
        try {
            if (paramsMap != null && paramsMap.size() > 0) {
                List<NameValuePair> params = new ArrayList<NameValuePair>();
                for (Map.Entry<String, String> m : paramsMap.entrySet()) {
                    params.add(new BasicNameValuePair(m.getKey(), m.getValue()));
                }
                UrlEncodedFormEntity reqEntity = new UrlEncodedFormEntity(params, "UTF-8");
                post.setEntity(reqEntity);
            }
            HttpResponse response = getHttpClient().execute(post);
            HttpEntity resEntity = response.getEntity();
            int statusCode = response.getStatusLine().getStatusCode();
            httpResult.setCode(statusCode);
            httpResult.setBody(getString(resEntity));
            if (statusCode != OK) {
                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
                post.abort();
                return httpResult;
            }
        } catch (SocketTimeoutException e) {
            httpResult.setCode(READ_TIMEOUT);
            return httpResult;
        } catch (IOException e) {
            logger.error("++++ doPost:" + url + " ++++++", e);
            httpResult.setCode(OTHER_IO_EXCEPTION);
            return httpResult;
        } catch (Exception e) {
            logger.error("++++ doPost:" + url + " ++++++", e);
            httpResult.setCode(INTERNAL_SERVER_ERROR);
            return httpResult;
        } finally {
            post.releaseConnection();
            MonitorTime(url, currentTime);
        }
        return httpResult;
    }

    private static String getString(HttpEntity resEntity) {
        String result = null;
        if (resEntity != null) {
            try {
                result = EntityUtils.toString(resEntity);
            } catch (Exception e) {
                logger.error("+++++==> respBody:" + result + " <==+++++", e);
            }
        }
        return result;
    }

    /**
     * 访问服务
     *
     * @param url 地址
     * @param xml
     * @return
     * @throws Exception
     */
    public static HttpResult doPostXml(String url, String xml) throws Exception {
        return doPostXml(url, "UTF-8", xml);
    }

    public static HttpResult doPostXml(String url, String charSet, String xml) throws Exception {
        long currentTime = System.currentTimeMillis();
        HttpResult httpResult = new HttpResult();
        HttpPost post = new HttpPost(url);
        // 然后把Soap请求数据添加到PostMethod中
        try {
            charSet = charSet == null ? "UTF-8" : charSet;
            byte[] b = xml.getBytes(charSet);
            InputStream is = new ByteArrayInputStream(b, 0, b.length);
            HttpEntity reqEntity = new InputStreamEntity(is, b.length, ContentType.create(ContentType.TEXT_XML.getMimeType(), Charset.forName(charSet)));
            post.setEntity(reqEntity);
            HttpResponse response = getHttpClient().execute(post);
            HttpEntity resEntity = response.getEntity();
            int statusCode = response.getStatusLine().getStatusCode();
            httpResult.setCode(statusCode);
            httpResult.setBody(getString(resEntity));
            if (statusCode != OK) {
                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
                post.abort();
                return httpResult;
            }
        } catch (SocketTimeoutException e) {
            httpResult.setCode(READ_TIMEOUT);
            return httpResult;
        } catch (IOException e) {
            logger.error("++++ doPostXml:" + url + " ++++++", e);
            httpResult.setCode(OTHER_IO_EXCEPTION);
            return httpResult;
        } catch (Exception e) {
            logger.error("++++ doPost:" + url + " ++++++", e);
            httpResult.setCode(INTERNAL_SERVER_ERROR);
            return httpResult;
        } finally {
            post.releaseConnection();
            MonitorTime(url, currentTime);
        }
        return httpResult;
    }

    /**
     * 根据URL发送post请求获取数据
     * Rest
     *
     * @param url
     * @return
     */
    public static HttpResult doPostJson(String url, String json) {
        long currentTime = System.currentTimeMillis();
        HttpResult httpResult = new HttpResult();
        HttpPost post = new HttpPost(url);
        try {
            post.addHeader("content-type", "application/json");
            if (SysUtil.isNotEmpty(json)) {
                StringEntity myEntity = new StringEntity(json, "UTF-8");
                post.setEntity(myEntity);
            }
            HttpResponse response = getHttpClient().execute(post);

            HttpEntity resEntity = response.getEntity();
            int statusCode = response.getStatusLine().getStatusCode();
            httpResult.setCode(statusCode);
            httpResult.setBody(getString(resEntity));
            if (statusCode != OK) {
                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
                post.abort();
                return httpResult;
            }
        } catch (SocketTimeoutException e) {
            httpResult.setCode(READ_TIMEOUT);
            return httpResult;
        } catch (IOException e) {
            logger.error("++++ doPostJson:" + url + " ++++++", e);
            httpResult.setCode(OTHER_IO_EXCEPTION);
            return httpResult;
        } catch (Exception e) {
            logger.error("++++ doPost:" + url + " ++++++", e);
            httpResult.setCode(INTERNAL_SERVER_ERROR);
            return httpResult;
        } finally {
            post.releaseConnection();
            MonitorTime(url, currentTime);
        }
        return httpResult;
    }


    /**
     * 根据url下载文件
     *
     * @param url
     * @return
     */
    public static byte[] download(String url) {
        long currentTime = System.currentTimeMillis();
        HttpGet httpget = new HttpGet(url);
        try {
            HttpResponse response = getHttpClient().execute(httpget);
            HttpEntity resEntity = response.getEntity();
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != OK) {
                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
                httpget.abort();
                return null;
            }
            if (resEntity != null && resEntity.isStreaming()) {
                return IOUtils.toByteArray(resEntity.getContent());
            }
        } catch (IOException e) {
            logger.error("+++++==> download:" + url + " <==+++++", e);
        } finally {
            httpget.releaseConnection();
            MonitorTime(url, currentTime);
        }
        return null;
    }

//
//    public static HttpResult upload(String url, byte[] data, String fileName, FileType fileType) {
//        return upload(url, data, fileName, fileType, null, null, null);
//    }
//
//    /**
//     * 发送文件到文件服务器
//     *
//     * @param data
//     * @return
//     */
//    public static HttpResult upload(String url, byte[] data, String fileName, FileType fileType, AccessType accessType, String owner, String groupName) {
//        long currentTime = System.currentTimeMillis();
//        HttpResult httpResult = new HttpResult();
//        if (data == null || data.length <= 0 || SysUtil.isEmpty(fileName)) {
//            return httpResult;
//        }
//        File tmpFile = null;
//        HttpPost post = new HttpPost(url);
//        try {
//            MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
//            if (fileType == null) {
//                fileType = FileType.NONE;
//            }
//            reqEntity.addPart("fileType", new StringBody(fileType.toString(), Charset.forName("UTF-8")));
//            reqEntity.addPart("name", new StringBody(fileName, Charset.forName("UTF-8")));//真实名称
//            if (accessType != null) {
//                reqEntity.addPart("accessType", new StringBody(accessType.toString(), Charset.forName("UTF-8")));
//            }
//            if (SysUtil.isNotEmpty(owner)) {
//                reqEntity.addPart("owner", new StringBody(owner, Charset.forName("UTF-8")));
//            }
//            if (SysUtil.isNotEmpty(groupName)) {
//                reqEntity.addPart("groupName", new StringBody(groupName, Charset.forName("UTF-8")));
//            }
//
//            tmpFile = FileUtils.getFileFromBytes(data, fileName);//临时文件名称
//            if (tmpFile != null && tmpFile.length() > 0)
//                reqEntity.addPart("file", new FileBody(tmpFile));
//
//            post.setEntity(reqEntity);
//
//            HttpResponse response = getHttpClient().execute(post);
//
//            HttpEntity resEntity = response.getEntity();
//            int statusCode = response.getStatusLine().getStatusCode();
//            httpResult.setCode(statusCode);
//            httpResult.setBody(getString(resEntity));
//            if (statusCode != OK) {
//                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
//                post.abort();
//                return httpResult;
//            }
//        } catch (SocketTimeoutException e) {
//            httpResult.setCode(READ_TIMEOUT);
//            return httpResult;
//        } catch (IOException e) {
//            logger.error("++++ upload:" + url + " ++++++", e);
//            httpResult.setCode(OTHER_IO_EXCEPTION);
//            return httpResult;
//        } finally {
//            post.releaseConnection();
//            if (tmpFile != null) {
//                tmpFile.delete();
//            }
//            MonitorTime(url, currentTime);
//        }
//        return httpResult;
//    }
//
//    /**
//     * for cdn
//     *
//     * @param url
//     * @param path
//     * @param name
//     * @param data
//     * @return
//     */
//    public static HttpResult uploadEcoImg(String url, String path, String name, byte[] data) {
//        long currentTime = System.currentTimeMillis();
//        HttpResult httpResult = new HttpResult();
//        if (data == null || data.length <= 0) {
//            return httpResult;
//        }
//        File tmpFile = null;
//        HttpPost post = new HttpPost(url);
//        try {
//            MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
//
//            reqEntity.addPart("path", new StringBody(path, Charset.forName("UTF-8")));
//            reqEntity.addPart("name", new StringBody(name, Charset.forName("UTF-8")));//真实名称
//
//            tmpFile = FileUtils.getFileFromBytes(data, name);//临时文件名称
//
//            if (tmpFile != null && tmpFile.length() > 0)
//                reqEntity.addPart("pic", new FileBody(tmpFile, name, "application/octet-stream", null));
//
//            post.setEntity(reqEntity);
//
//            HttpResponse response = getHttpClient().execute(post);
//
//            HttpEntity resEntity = response.getEntity();
//            int statusCode = response.getStatusLine().getStatusCode();
//            httpResult.setCode(statusCode);
//            httpResult.setBody(getString(resEntity));
//            if (statusCode != OK) {
//                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
//                post.abort();
//                return httpResult;
//            }
//        } catch (SocketTimeoutException e) {
//            httpResult.setCode(READ_TIMEOUT);
//            return httpResult;
//        } catch (IOException e) {
//            logger.error("++++ upload:" + url + " ++++++", e);
//            httpResult.setCode(OTHER_IO_EXCEPTION);
//            return httpResult;
//        } finally {
//            post.releaseConnection();
//            if (tmpFile != null) {
//                tmpFile.delete();
//            }
//            MonitorTime(url, currentTime);
//        }
//        return httpResult;
//    }
//
//    public static HttpResult uploadFileByDD(String url, String fileName, String xml) throws Exception {
//        long currentTime = System.currentTimeMillis();
//        HttpResult httpResult = new HttpResult();
//
//        File tmpFile = null;
//        HttpPost post = new HttpPost(url);
//        try {
//            String uuid = UUID.randomUUID() + "";
//            MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
//            tmpFile = FileUtils.readXML(fileName + uuid, xml, "GBK");
//            if (tmpFile != null && tmpFile.length() > 0)
//                reqEntity.addPart(fileName, new FileBody(tmpFile));
//
//            post.setEntity(reqEntity);
//
//            HttpResponse response = getHttpClient().execute(post);
//
//            HttpEntity resEntity = response.getEntity();
//            int statusCode = response.getStatusLine().getStatusCode();
//            httpResult.setCode(statusCode);
//            httpResult.setBody(getString(resEntity));
//            if (statusCode != OK) {
//                logger.error("+++++==>statusCode:[" + statusCode + "],url:" + url + " <==+++++");
//                post.abort();
//                return httpResult;
//            }
//        } catch (SocketTimeoutException e) {
//            httpResult.setCode(READ_TIMEOUT);
//            return httpResult;
//        } catch (IOException e) {
//            logger.error("++++ upload:" + url + " ++++++", e);
//            httpResult.setCode(OTHER_IO_EXCEPTION);
//            return httpResult;
//        } finally {
//            post.releaseConnection();
//            if (tmpFile != null) {
//                tmpFile.delete();
//            }
//            MonitorTime(url, currentTime);
//        }
//        return httpResult;
//    }

    private static HttpClient getHttpClient() {
        HttpClient httpClient = new DefaultHttpClient(clientConnectionManager);
        httpClient.getParams().setParameter(SOCKET_TIMEOUT, TIME_OUT);
        httpClient.getParams().setParameter(COLLECTION_TIMEOUT, TIME_OUT);
        httpClient.getParams().setParameter(COLLECTION_MANAGER_TIMEOUT, 10000000l);
        httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
        return httpClient;
    }

    private static SSLSocketFactory createSSLSocketFactory() {
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            ctx.init(null, new TrustManager[]{tm}, null);
            return new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        } catch (Exception e) {
            logger.error("createSSLSocketFactory error!", e);
        }
        return null;
    }

}